package cn.bigcontainer.alt.meta.dto;

import java.util.*;
import java.util.stream.Collectors;

import com.fasterxml.jackson.annotation.JsonIgnore;

import cn.bigcontainer.alt.common.entity.BigMap;
import cn.bigcontainer.alt.common.entity.WoPage;
import cn.bigcontainer.alt.common.util.WoJsonKit;
import cn.bigcontainer.alt.common.util.WoKit;
import cn.bigcontainer.alt.meta.exception.MException;
import cn.bigcontainer.alt.meta.extension.MFormKeyValueAdaptor;
import cn.bigcontainer.alt.meta.extension.MKeyCreator;
import cn.bigcontainer.alt.meta.extension.MListDataFilter;
import cn.bigcontainer.alt.meta.po.MButton;
import cn.bigcontainer.alt.meta.po.MObject;
import cn.bigcontainer.alt.meta.po.MProperty;
import cn.bigcontainer.alt.meta.po.MView;
import cn.bigcontainer.alt.meta.po.MViewProperty;
import cn.bigcontainer.alt.meta.util.MUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @author jiaod
 *
 */
@Slf4j
public class MViewDto extends MView implements Comparable<MViewDto> {

    public static final String TYPE_LIST = "list";

    public static final String TYPE_FORM = "form";

    public static final String TYPE_SEARCH = "search";

    /**
     * 表单加载时，如果没有提供主键值，则使用本参数配置的bean来获取
     */
    public static final String PARAM_KV_ADAPTOR = "keyAdaptor";
    
    /**
     * 列表的最大行数
     */
    public static final String PARAM_PAGE_SIZE = "pageSize";

    /**
     * 列表或者数据
     */
    public static final String PARAM_LIST_DATA = "listData";

    /**
     * 列表数据的总数
     */
    // public static final String PARAM_TOTAL = "totals";

    /**
     * 列表是否分页
     */
    public static final String PARAM_PAGING = "paging";

    /**
     * 主键创建bean
     */
    public final static String PARAM_KEY_CREATOR = "keyCreator";

    /**
     * 表单提交后返回的数据
     */
    public final static String PARAM_CALL_BACK = "callBackData";

    /**
     * 列表视图中选中行的id值，多个以逗号隔开
     */
    public final static String PARAM_SELECTED = "selectedIds";

    /**
     * 表单提交后返回的数据
     */
    public final static String PARAM_READONLY = "readonly";

    /**
     * 列表操作列宽度
     */
    public static final String PARAM_OPERATION_WIDTH = "operationColumnWidth";
    
    @JsonIgnore
    private MAppDto app;

    public MViewDto() {
    }

    public MViewDto(MView po) {
        super(po);
    }

    public MViewDto(MView po, MAppDto app) {
        super(po);
        this.app = app;
    }

    @Override
    public int compareTo(MViewDto o) {
        return this.getIdx() - o.getIdx();
    }

    public MViewProperty getViewPropertyByPropertyId(String propertyId) {
    	Optional<MViewProperty> findFirst = this.getProperties().stream().filter(p -> propertyId != null && propertyId.equals(p.getPropertyId())).findFirst();
        return findFirst.isPresent() ? findFirst.get() : null;
    }

    @JsonIgnore
    public List<MViewProperty> getKeyProperties() {
        return getKeyProperties(this.app.getObject(), this);
    }
    
    @JsonIgnore
    public MViewProperty getKeyProperty() {
    	List<MViewProperty> keyProperties = getKeyProperties();
    	if (keyProperties.size() == 0) {
    		return null;
    	}
        return keyProperties.get(0);
    }

    public static List<MViewProperty> getKeyProperties (MObject object, MView view) {
        return view.getProperties().stream().filter(e -> {
            MProperty property = MObjectDto.getProperty(object, e.getPropertyId());
            if (property != null && property.getKey() == 1) {
                return true;
            }
            return false;
        }).collect(Collectors.toList());
    }

    /**
     * 表单提交时，从request中获取key值
     *
     * @return
     */
    @JsonIgnore
    public Object getKeyValue() {
        BigMap<String, Object> request = getApp().getObject().getRequest();
        List<MViewProperty> keyProperties = getKeyProperties();
        if (keyProperties.size() == 1) {
            return request.get(keyProperties.get(0).getId());
        }
        return null;
    }

    @JsonIgnore
    public MViewProperty getNoProperty() {
        MProperty noProperty = getApp().getObject().getNoProperty();
        if (noProperty == null) {
            return null;
        }
        return this.getProperties().stream().filter(e -> noProperty.getId().equals(e.getPropertyId())).findFirst().get();
    }

    public List<MViewProperty> getProperties() {
        properties.stream().forEach(e -> e.setView(this));
        return properties;
    }

    public List<MButton> getButtons() {
        buttons.stream().forEach(e -> e.setView(this));
        return buttons;
    }

    @JsonIgnore
    public MListDataFilter getListDataFilter() {
        return MObjectDto.getListFilter(this.id, data, getApp().getObject().getRequest());
    }

    @JsonIgnore
    public Boolean paging() {
        return !"false".equals(data.get(PARAM_PAGING));
    }

    @JsonIgnore
    public Long getPageSize() {
        return data.getLong(PARAM_PAGE_SIZE, MUtil.getPageSize());
    }

    @JsonIgnore
    public WoPage<Map<String, Object>> getListData() {
        return (WoPage<Map<String, Object>>) data.get(PARAM_LIST_DATA);
    }

    public void setListData(WoPage<Map<String, Object>> pageData) {
        data.put(PARAM_LIST_DATA, pageData);
    }

    /**
     * @param row    行号，0开始
     * @param propId 视图属性id
     * @return
     */
    public Object getListValue(int row, String propId) {
        List<Map<String, Object>> listData = this.getListData().getRows();
        Map<String, Object> rowData = listData.get(row);
        // MProperty.TYPE_DATETIME
        MViewProperty property = getProperty(propId);
        if (property == null) {
            return null;
        }
        MProperty mProperty = property.getProperty();
        Object val = rowData.get(propId);
        if (!WoKit.isEmpty(val) && mProperty != null && mProperty.getType().equals(MProperty.TYPE_DATETIME)) {
            val = val.toString().replace('T', ' ');
        }
        return val;
    }
    
    /**
     * @param row
     * @param suffix
     * @return
     */
    public Object getListValueBySuffix(int row, String suffix) {
    	MViewProperty viewProperty = getPropertyBySuffix(suffix);
    	if (viewProperty == null) {
    		return null;
    	}
    	return getListValue(row, viewProperty.getId());
    }

    public static MViewProperty getPropertyBySuffix(MView view, String suffix) {
        for (MViewProperty vp : view.getProperties()) {
            if (vp.getId().endsWith(suffix)) {
                return vp;
            }
        }
        return null;
    }

    public MViewProperty getPropertyBySuffix(String suffix) {
        return getPropertyBySuffix(this, suffix);
	}

	public Object getListValue(int row, int propIndex) {
        String propId = this.getProperties().get(propIndex).getId();
        return getListValue(row, propId);
    }

    public String getListRowJson(int row) {
        return WoJsonKit.writeAsString(getListData().getRows().get(row));
    }

    /**
     * 列表是否单选
     *
     * @return
     */
    @JsonIgnore
    public Boolean isListSingleSelect() {
        for (MButton b : this.buttons) {
            String selectType = b.getData().getString(MButton.PARAM_SEL, MButton.SEL_NONE);
            if (selectType.startsWith(MButton.SEL_MULTIPLE)) {
                return false;
            }
        }
        return true;
    }

    @JsonIgnore
    public MViewProperty getNameProperty() {
        MProperty np = getApp().getObject().getNameProperty();
        if (np == null) {
            return null;
        }
        return this.getProperties().stream().filter(e -> np.getId().equals(e.getPropertyId())).findFirst().get();
    }

    /**
     * @return
     */
    public List<MViewProperty> getProps(List<MProperty> props) {
        List<MViewProperty> results = new ArrayList<MViewProperty>();
        for (MProperty p : props) {
            MViewProperty fp = this.getViewPropertyByPropertyId(p.getId());
            if (fp == null) {
                throw new MException(MException.VIEW_PROP, p.getId());
            }
            results.add(fp);
        }
        return results;
    }

    /**
     * @return
     */
    public Object[] getValues(List<MProperty> props) {
        List<MViewProperty> fps = this.getProps(props);
        Object[] vals = new Object[fps.size()];
        for (int i = 0; i < vals.length; i++) {
            MViewProperty fp = fps.get(i);
            vals[i] = fp.getValue();
        }
        return vals;
    }

    /**
     * 为主键创建默认值
     */
    public void createKeyValues() {
        String creator = this.data.getString(PARAM_KEY_CREATOR);
        if (WoKit.isEmpty(creator)) {
            creator = "woKeyCreator";
        }
        MKeyCreator impl = WoKit.getBean(MKeyCreator.class, creator);
        impl.createKeyValues(this);
    }

    /**
     * 设置主键的值
     *
     * @param keyValues 如果是多个主键值，则逗号隔开
     */
    public void setKeyValuesString(String keyValues) {
        if (WoKit.isEmpty(keyValues)) {
            throw new MException(MException.PK_VALUE, this.getId());
        }
        String[] keys = keyValues.split(",");
        List<MViewProperty> keyProps = this.getKeyProperties();
        if (keyProps.size() != keys.length) {
            throw new MException(MException.PK_NUM, this.getId(), keyValues);
        }
        for (int i = 0; i < keys.length; i++) {
            if (WoKit.isEmpty(keys[i])) {
                throw new MException(MException.PK_VALUE, this.getId());
            }
            keyProps.get(i).setValue(keys[i]);
        }
    }

    /**
     * 设置主键的值
     *
     * @param req request数据
     */
    public void setKeyValuesByRequest(BigMap<String, Object> req) {
        List<MViewProperty> keyProps = this.getKeyProperties();
        for (int i = 0; i < keyProps.size(); i++) {
            MViewProperty k = keyProps.get(i);
            String val = req.getString(k.getId());
            if (WoKit.isEmpty(val)) {
                throw new MException(MException.PK_VALUE, this.getId());
            }
            k.setValue(val);
        }
    }

    /**
     * form加载时获取数据
     *
     * @return
     */
    @JsonIgnore
    public Object[] getKeyValues() {
        List<MViewProperty> keys = this.getKeyProperties();
        Object[] keyVals = new Object[keys.size()];
        for (int i = 0; i < keyVals.length; i++) {
            MViewProperty key = keys.get(i);
            keyVals[i] = key.getValue();
            if (WoKit.isEmpty(keyVals[i])) {
                throw new MException(MException.PK_VALUE, this.getId());
            }
        }
        return keyVals;
    }

    /**
     * @return
     */
    @JsonIgnore
    public String getUIKeyValues() {
        List<MViewProperty> keys = this.getKeyProperties();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < keys.size(); i++) {
            MViewProperty key = keys.get(i);
            if (sb.length() > 0) {
                sb.append(',');
            }
            sb.append(key.getUIValue());
        }
        return sb.toString();
    }

    /**
     * 表单提交后返回的json中，content节点会返回本方法的对象
     *
     * @return
     */
    @JsonIgnore
    public Object getCallBackData() {
        Object val = this.data.getString(PARAM_CALL_BACK);
        if (WoKit.isEmpty(val)) {
            val = this.getUIKeyValues();
        }
        return val;
    }

    /**
     * @param val
     */
    public void setCallBackData(Object val) {
        this.data.put(PARAM_CALL_BACK, val);
    }

    /**
     * (xxx.key1, xxx.key2) = (val1, val2)
     *
     * @return
     */
    public String getSqlPKCondition(boolean hasAlias) {
        List<MViewProperty> keyProps = this.getKeyProperties();
        String fs = "";
        String vs = "";
        for (int i = 0; i < keyProps.size(); i++) {
            MViewProperty fp = keyProps.get(i);
            MProperty key = fp.getPropertyIncludeRelatedObjects();
            if (!fs.equals("")) {
                fs += ",";
                vs += ",";
            }
            if (hasAlias) {
                fs += getApp().getObject().getNo() + ".";
            }
            fs += key.getName();
            Object val = fp.getValue();
            if (WoKit.isEmpty(val)) {
                throw new MException(MException.PK_VALUE, this.getId());
            }
            vs += key.getSqlValue(val.toString());
        }
        return String.format("(%s) = (%s)", fs, vs);
    }

    /**
     * Form参数
     *
     * @return
     */
    @JsonIgnore
    public String getEncType() {
        for (MViewProperty p : this.getProperties()) {
            if (MViewProperty.TYPE_FILE.equals(p.getType()) || MViewProperty.TYPE_IMAGE.equals(p.getType())) {
                return "multipart/form-data";
            }
        }
        return "application/x-www-form-urlencoded";
    }

    @JsonIgnore
    public MAppDto getApp() {
        return app;
    }

    public void setApp(MAppDto app) {
        this.app = app;
    }

    @JsonIgnore
    public Boolean getMetaShow() {
        return data.getBoolean(MObjectDto.PARAM_META_SHOW, false);
    }

    public void setMetaShow(Boolean metaShow) {
        data.put(MObjectDto.PARAM_META_SHOW, metaShow);
    }

    @JsonIgnore
    public Boolean getMetaShowProperties() {
        for (MViewProperty vp : properties) {
            if (vp.getMetaShow()) {
                return true;
            }
        }
        return false;
    }

    @JsonIgnore
    public Boolean getMetaShowButtons() {
        for (MButton b : buttons) {
            if (b.getMetaShow()) {
                return true;
            }
        }
        return false;
    }

    /**
     * 在元数据管理页面，当前视图tab是否active
     *
     * @return
     */
    @JsonIgnore
    public Boolean getMetaActive() {
        return getMetaShow() || getMetaShowProperties() || getMetaShowButtons();
    }

    @JsonIgnore
    public Integer getReadonly() {
        return data.getInt(PARAM_READONLY, 0);
    }

    public void setReadonly(Integer readonly) {
        data.put(PARAM_READONLY, readonly);
    }
    
    @JsonIgnore
    public List<MViewProperty> getOrderProperties () {
    	List<MViewProperty> orderList = new ArrayList<MViewProperty>();
    	this.getProperties().forEach(p -> {
    		if (p.getOrderIndex() > -1) {
    			orderList.add(p);
    		}
    	});
    	Collections.sort(orderList, new Comparator<MViewProperty>() {
			@Override
			public int compare(MViewProperty p0, MViewProperty p1) {
				return p0.getOrderIndex() - p1.getOrderIndex();
			}
		});
    	return orderList;
    }
    
    public void reIndexProperties () {
    	for (int i = 0; i < properties.size(); i++) {
			properties.get(i).setIndex((i + 1) * 10);
		}
    }
    
    public void reIndexButtons () {
    	for (int i = 0; i < buttons.size(); i++) {
			buttons.get(i).setIndex((i + 1) * 10);
		}
    }
    
    /**
     * @param vpId
     * @return
     */
    public MViewProperty getProperty (String vpId) {
    	for (int i = 0; i < getProperties().size(); i++) {
			if (getProperties().get(i).getId().equals(vpId)) {
				return getProperties().get(i);
			}
		}
    	return null;
    }
    
    
    /**
     * @param buttonId
     * @return
     */
    public MButton getButton (String buttonId) {
    	for (int i = 0; i < getButtons().size(); i++) {
			if (getButtons().get(i).getId().equals(buttonId)) {
				return getButtons().get(i);
			}
		}
    	return null;
    }
    
    @JsonIgnore
    public Boolean isList () {
    	return TYPE_LIST.equals(type);
    }
    
    @JsonIgnore
    public Boolean isForm () {
    	return TYPE_FORM.equals(type);
    }
    
    @JsonIgnore
    public Boolean isSearch () {
    	return TYPE_SEARCH.equals(type);
    }
    
    /**
     * @return
     */
    public Boolean hasVisibleProperties () {
    	return hasVisibleProperties(this);
    }
    
    public static Boolean hasVisibleProperties (MView mView) {
    	List<MViewProperty> properties2 = mView.getProperties();
    	for (int i = 0; i < properties2.size(); i++) {
    		if (!MViewProperty.TYPE_HIDDEN.equals(properties2.get(i).getType())) {
    			return true;
    		}
		}
    	return false;
    }
    
    /**
     * @return
     */
    @JsonIgnore
    public List<MViewProperty> getIconProperties () {
    	return this.getProperties().stream().filter(vp -> MViewProperty.TYPE_ICON.equals(vp.getType())).collect(Collectors.toList());
    }
    
    @JsonIgnore
    public MViewProperty getIconProperty () {
    	List<MViewProperty> iconProperties = getIconProperties ();
    	if (iconProperties.size() == 0) {
    		return null;
    	}
    	return iconProperties.get(0);
    }
    
    @JsonIgnore
    public Integer getOperationColumnWidth() {
    	if (data.getInt(PARAM_OPERATION_WIDTH) != null) {
    		return data.getInt(PARAM_OPERATION_WIDTH);
    	}
    	Integer width = 0;
    	// 80 50
    	for (MButton btn : this.getButtons()) {
			if (btn.isSingleSelect()) {
				width += 60 + btn.getName().length() * 10;
				if (getApp().getObject().isMeta()) {
				    width += 40;
                }
			}
		}
    	if (width == 0) {
    		width = 60;
    	}
    	return width;
    }

    /**
     * 列表数据过滤器使用的sql中的表别名
     * @return
     */
    @JsonIgnore
	public String getObjectAlias() {
		return objectId.substring(objectId.indexOf('_') + 1);
	}
    
    /**
     * @return
     */
    @JsonIgnore
    public Map<String, Object> getFormData () {
    	Map<String, Object> map = new HashMap<String, Object>();
    	getProperties().forEach(p -> {
    		if (!WoKit.isEmpty(p.getValue())) {
    			map.put(p.getId(), p.getValue());
    		}
    	});
    	return map;
    }
    
    /**
     * 从request中根据字段id后缀获取值
     * @param propIdSuffix
     * @return
     */
    public Object getSubmitValueBySuffix (String propIdSuffix) {
    	BigMap<String,Object> request = getApp().getObject().getRequest();
    	String key = getObjectId() + "_" + propIdSuffix;
    	if (request.containsKey(key)) {
    		return request.get(key);
    	}
    	key = getId() + "_" + propIdSuffix;
    	return request.get(key);
    }
    
    public void setSubmitValueBySuffix (String propIdSuffix, Object val) {
    	BigMap<String,Object> request = getApp().getObject().getRequest();
    	String key = getObjectId() + "_" + propIdSuffix;
    	if (request.containsKey(key)) {
    		request.put(key, val);
    	}
    	key = getId() + "_" + propIdSuffix;
    	if (request.containsKey(key)) {
    		request.put(key, val);
    	}
    }
    
    /**
     * @param propIdSuffix
     * @return
     */
    public String getSubmitStringValueBySuffix (String propIdSuffix) {
    	Object value = getSubmitValueBySuffix(propIdSuffix);
    	if (value == null) {
    		return null;
    	}
    	return value.toString();
    }
    
    /**
     * @return
     */
    public MFormKeyValueAdaptor getKeyValueAdaptor() {
		String adaptorString = data.getString(PARAM_KV_ADAPTOR);
		if (WoKit.isEmpty(adaptorString)) {
			return WoKit.getBean(MFormKeyValueAdaptor.class);
		}
		return WoKit.getBean(MFormKeyValueAdaptor.class, adaptorString);
	}

	public static void removeProperty (MView view, MViewProperty viewProperty) {
        view.getProperties().remove(viewProperty);
    }

    public void removeProperty (MViewProperty viewProperty) {
        removeProperty(this, viewProperty);
    }

    public static void removePropertyBySuffix (MView view, String suffix) {
        MViewProperty viewProperty = MViewDto.getPropertyBySuffix(view, suffix);
        removeProperty(view, viewProperty);
    }

    public void removePropertyBySuffix (String suffix) {
        removePropertyBySuffix (this, suffix);
    }

    /**
     * 根据按钮类型删除按钮
     * @param btnType
     */
    public void removeButtonByType (String btnType) {
        Iterator<MButton> iterator = buttons.iterator();
        while (iterator.hasNext()) {
            MButton button = iterator.next();
            if (button.getType().equals(btnType)) {
                iterator.remove();
            }
        }
    }
}
